#include "./ftp_projrct/include/transfer.h"
#include "./ftp_projrct/include/protocol.h"
#include "./ftp_projrct/include/command.h"
#define MMAP_THRESHOLD (100*1024*1024)
int recvn(int sockfd, void *buf, int length);

int transfile(const char *filename, int sockfd){
    TLVMsg train;
    FileTransferCtx filetrans;
    
    train.value_len = strlen(filename);
    memcpy(train.value,filename,train.value_len);
    send(sockfd,&train.value_len,sizeof(train.value_len),MSG_NOSIGNAL);
    send(sockfd,train.value,train.value_len,MSG_NOSIGNAL);
    
    int fd = open(filename,O_RDWR);
    struct stat statbuf;
    fstat(fd,&statbuf);
    filetrans.total_size = statbuf.st_size;
    train.value_len = sizeof(filetrans.total_size);
    memcpy(train.value,&filetrans.total_size,train.value_len);
    send(sockfd,&train.value_len,sizeof(train.value_len),MSG_NOSIGNAL);
    send(sockfd,train.value,train.value_len,MSG_NOSIGNAL);
    
    off_t download_size = 0;
    if(recvn(sockfd,&download_size,sizeof(download_size)) == -1){
        close(fd);
        return -1;
    };
    if(filetrans.total_size > MMAP_THRESHOLD && download_size == 0){
        printf("file size > 100mb, using mmap for transfer.\n");
        char *p = (char *)mmap(NULL,filetrans.total_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        send(sockfd,p,filetrans.total_size,0);
        munmap(p,filetrans.total_size);
    }
    else{
        lseek(fd,download_size,SEEK_SET);
        while(1){
            bzero(train.value,sizeof(train.value));
            ssize_t sret = read(fd,train.value,sizeof(train.value));
            train.value_len = sret;
            send(sockfd,&train.value_len,sizeof(train.value_len),MSG_NOSIGNAL);
            send(sockfd,train.value,train.value_len,MSG_NOSIGNAL);
            if(sret == 0){
                break;
            }
        }
    }
    close(fd);
    return 0;
}


int recvn(int sockfd, void *buf, int length){
    char *p = (char *)buf;
    ssize_t total = 0;
    while(total < length){
        ssize_t sret = recv(sockfd,p+total,length-total,0);
        if(sret == 0){
            return -1;
        }
        total += sret;
    }
    return 0;
}

int recvfile(int sockfd){
    TLVMsg train;
    FileTransferCtx filetrans;

    recvn(sockfd,&train.value_len,sizeof(train.value_len));
    recvn(sockfd,train.value,train.value_len);
    char filename[4096] = {0};
    
    memcpy(filename,train.value,train.value_len);
    off_t download_size = 0;
    struct stat statbuf;
    int fd = open(filename,O_RDWR|O_CREAT,0666);
    if(fstat(fd,&statbuf) == 0){
        download_size = statbuf.st_size;
    }
    lseek(fd,download_size,SEEK_SET);

    recvn(sockfd,&train.value_len,sizeof(train.value_len));
    recvn(sockfd,train.value,train.value_len);
    memcpy(&filetrans.total_size, train.value, train.value_len);
    send(sockfd,&download_size,sizeof(download_size),MSG_NOSIGNAL);
    if(train.value_len > MMAP_THRESHOLD && download_size == 0){
        ftruncate(fd,filetrans.total_size);
        char *p = (char *)mmap(NULL,filetrans.total_size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
        recvn(sockfd,p,filetrans.total_size);
        printf("100.00%%\n");
        munmap(p,filetrans.total_size);
    }
    else{
        off_t cursize = download_size;
        off_t slice = filetrans.total_size/10000;
        off_t lastsize = download_size;
        while(1){
            bzero(&train,sizeof(train));
            if(recvn(sockfd,&train.value_len,sizeof(train.value_len)) == -1){
                break;
            };
            cursize += train.value_len; 
            if(cursize - lastsize > slice){
                printf("%5.2lf%%\r",cursize*100.0/filetrans.total_size);
                fflush(stdout); 
                lastsize = cursize;
            }
            if(train.value_len != 1000){
                printf("train.value_len = %d\n",train.value_len);
            }
            if(train.value_len == 0){
                break;
            }
            if(recvn(sockfd,train.value,train.value_len) == -1){
                break;
            };
            write(fd,train.value,train.value_len);
        }
    }
    printf("100.00%%\n");
    close(fd);
    return 0;
}
